package org.bgee.psp4jdbc.jdbcapi;

import java.io.PrintWriter;
import java.sql.SQLException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bgee.psp4jdbc.Properties;
import org.bgee.psp4jdbc.pool.Pool.EvictionMethod;

/** 
 * The aim of this class is to wrap a real <code>javax.sql.DataSource</code>, 
 * to return {@link Connection}s wrapping real <code>java.sql.Connection</code>s 
 * obtained from the real <code>javax.sql.DataSource</code>, and that hold 
 * a <code>PreparedStatement</code> pool.
 * 
 * @author Mathieu Seppey
 * @version 1, August 2013
 * @see Connection
 * @since 1
 *
 */
public class DataSource implements javax.sql.DataSource
{

    /**
     * <code>Logger</code> of the class. 
     */
    private final static Logger log = LogManager.getLogger(DataSource.class.getName());

    /**
     * The real <code>javax.sql.DataSource</code> that this class wraps. 
     */
    private javax.sql.DataSource realDataSource ;
    
    /**
     * Properties to delegate setters and getters calls to.
     */
    private final Properties properties;

    /**
     * Private constructor because a real <code>DataSource</code> must be provided.
     * @see #DataSource(javax.sql.DataSource)
     */
    //a real DataSource must be provided
    @SuppressWarnings("unused")
	private DataSource() {
    	this(null);
    }
    /**
     * Constructor providing the real <code>javax.sql.DataSource</code> 
     * that this class wraps.
     * 
     * @param realDataSource    The <code>javax.sql.DataSource</code> that this class wraps
     */
    public DataSource(javax.sql.DataSource realDataSource)
    {
        log.entry(realDataSource);
        this.setRealDataSource(realDataSource);
        this.properties = new Properties();
        log.exit();
    }

    @Override
    public Connection getConnection() throws SQLException
    {
    	log.entry();
    	log.trace("Gettng a Connection from the real DataSource {}", this.getRealDataSource());
    	return log.exit(new Connection(this.getRealDataSource().getConnection(), 
    			this.properties));
    }

    @Override
    public Connection getConnection(String username, String password)
            throws SQLException
    {
    	log.entry();
    	log.trace("Gettng a Connection from the real DataSource {}", this.getRealDataSource());
        return log.exit(new Connection(
        		this.getRealDataSource().getConnection(username,password), 
        		this.properties));
    }


    /**
	 * @return The real <code>javax.sql.DataSource</code> that this class wraps. 
	 */
	private javax.sql.DataSource getRealDataSource() {
		return this.realDataSource;
	}
	/**
	 * @param realDataSource 	The real <code>javax.sql.DataSource</code> 
	 * 							that this class wraps. 
	 */
	public void setRealDataSource(javax.sql.DataSource realDataSource) {
		this.realDataSource = realDataSource;
	}

    //***********************************
    // Methods delegated to Properties.
    //***********************************
    
	/*
     * method private on purpose, the real DataSource should take care 
     * of obtaining connections.
	 */
	@SuppressWarnings("unused")
	private String getPspDriverClassName() {
		return properties.getDriverClassName();
	}
	/*
     * method private on purpose, the real DataSource should take care 
     * of obtaining connections.
	 */
	@SuppressWarnings("unused")
	private void setPspDriverClassName(String underlyingDriver) {
		properties.setDriverClassName(underlyingDriver);
	}
	/**
	 * @return	see {@link org.bgee.psp4jdbc.Properties#getPoolMaxSize()}
	 * @see org.bgee.psp4jdbc.Properties#getPoolMaxSize()
	 */
	public int getPspPoolMaxSize() {
		return properties.getPoolMaxSize();
	}
	/**
	 * @param poolMaxSize	see {@link org.bgee.psp4jdbc.Properties#setPoolMaxSize(int)}
	 * @see org.bgee.psp4jdbc.Properties#setPoolMaxSize(int)
	 */
	public void setPspPoolMaxSize(int poolMaxSize) {
		properties.setPoolMaxSize(poolMaxSize);
	}
	/**
	 * @return	see {@link org.bgee.psp4jdbc.Properties#getGlobalMaxSize()}
	 * @see org.bgee.psp4jdbc.Properties#getGlobalMaxSize()
	 */
	public int getPspGlobalMaxSize() {
		return Properties.getGlobalMaxSize();
	}
	/**
	 * Warning, for the sake of avoiding static setters, this method is
	 * an instance setter, but will affect the entire application, 
	 * see {@link org.bgee.psp4jdbc.Properties#setGlobalMaxSize(int)}.
	 * 
	 * @param globalMaxSize 	see {@link org.bgee.psp4jdbc.Properties#setGlobalMaxSize(int)}
	 * @see org.bgee.psp4jdbc.Properties#setGlobalMaxSize(int)
	 */
	public void setPspGlobalMaxSize(int globalMaxSize) {
		Properties.setGlobalMaxSize(globalMaxSize);
	}
	/**
	 * @return	see {@link org.bgee.psp4jdbc.Properties#getEvictionMethod()}
	 * @see org.bgee.psp4jdbc.Properties#getEvictionMethod()
	 */
	public EvictionMethod getPspEvictionMethod() {
		return properties.getEvictionMethod();
	}
	/**
	 * @param evictionMethod	see {@link org.bgee.psp4jdbc.Properties#setEvictionMethod(
	 * 		org.bgee.psp4jdbc.pool.Pool.EvictionMethod)}
	 * @see org.bgee.psp4jdbc.Properties#setEvictionMethod(
	 * 		org.bgee.psp4jdbc.pool.Pool.EvictionMethod)
	 */
	public void setPspEvictionMethod(EvictionMethod evictionMethod) {
		properties.setEvictionMethod(evictionMethod);
	}
	/**
	 * @param evictionMethod	see {@link org.bgee.psp4jdbc.Properties#setEvictionMethod(
	 * 		org.bgee.psp4jdbc.pool.Pool.EvictionMethod)}
	 * @see org.bgee.psp4jdbc.Properties#setEvictionMethod(
	 * 		org.bgee.psp4jdbc.pool.Pool.EvictionMethod)
	 */
	public void setPspEvictionMethod(String evictionMethod) {
		properties.setEvictionMethod(EvictionMethod.valueOf(evictionMethod.trim()));
	}
	/**
	 * @return	see {@link org.bgee.psp4jdbc.Properties#getEvictionFactor()}
	 * @see org.bgee.psp4jdbc.Properties#getEvictionFactor()
	 */
	public float getPspEvictionFactor() {
		return properties.getEvictionFactor();
	}
	/**
	 * @param evictionFactor	see {@link org.bgee.psp4jdbc.Properties#setEvictionFactor(float)}
	 * @see org.bgee.psp4jdbc.Properties#setEvictionFactor(float)
	 */
	public void setPspEvictionFactor(float evictionFactor) {
		properties.setEvictionFactor(evictionFactor);
	}


    //***********************************
    // Methods that are simply delegated.
    //***********************************
	
	
	@Override
    public PrintWriter getLogWriter() throws SQLException
    {
        return this.getRealDataSource().getLogWriter();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException
    {
        return this.getRealDataSource().unwrap(iface);
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException
    {
    	this.getRealDataSource().setLogWriter(out);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException
    {
        return this.getRealDataSource().isWrapperFor(iface);
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException
    {
    	this.getRealDataSource().setLoginTimeout(seconds);
    }

    @Override
    public int getLoginTimeout() throws SQLException
    {
        return this.getRealDataSource().getLoginTimeout();
    }
}
